// 本文件实现生产者与消费者问题的多线程问题

#include <my_header.h>
#include "linked_queue.h"

#define MAX_SIZE 10  // 商品队列最大容量

// 定义共享资源结构体
typedef struct sharedRes_s{
    queue_t queue; // 商品队列
    pthread_mutex_t mutex; // 互斥锁
    pthread_cond_t producers_wait; // 生产者等待条件变量
    pthread_cond_t consumers_wait; // 消费者等待条件变量
}sharedRes_t;

// 生产行为：生成一个商品(随机数)，并放入队列中
int make_product(queue_t *queue){
    // 生成一个[1,100]的随机整数，不同数字表示不同商品
    int data = rand() % 100 + 1;
    // 将商品放入队列中
    int ret = queue_append(queue, data);
    if(ret != 0){
        return -1; // 未插入成功
    }
    return data; // 成功，返回刚生成的商品的值
}

/* ---------------------------生产者线程--------------------------- */
// 生产者：每3秒生产一个商品
void *producer(void *arg){
    // 获取共享资源
    sharedRes_t *resource = (sharedRes_t *)arg;
    while(1){
        pthread_mutex_lock(&resource->mutex); 
        /* ----------------------以下是临界区---------------------- */
        // 生产者阻塞判断
        if(resource->queue.size >= resource->queue.capacity){ // 队列已满
            pthread_cond_wait(&resource->producers_wait, &resource->mutex);
        }
        // 向商品队列中添加一个商品
        int product = make_product(&resource->queue); 
        printf("生产者添加了一个商品：%d\n商品队列：", product);
        queue_print(&resource->queue);
        // 唤醒一个被阻塞的消费者
        pthread_cond_signal(&resource->consumers_wait);
        /* ----------------------以上是临界区---------------------- */
        pthread_mutex_unlock(&resource->mutex);

        sleep(3); 
    }
    pthread_exit(NULL);
}
/* ---------------------------生产者线程--------------------------- */


/* ---------------------------消费者线程--------------------------- */
// 消费者：先睡眠5秒，然后每1秒消耗一个商品
void *consumer(void *arg){
    // 获取共享资源
    sharedRes_t *resource = (sharedRes_t *)arg;
    sleep(5);
    while(1){
        pthread_mutex_lock(&resource->mutex); 
        /* ----------------------以下是临界区---------------------- */
        // 消费者阻塞判断
        if(resource->queue.size <= 0){ // 队列为空
            pthread_cond_wait(&resource->consumers_wait, &resource->mutex);
        }
        // 从商品队列中拿取一个商品
        int product = queue_pop(&resource->queue);
        printf("消费者获得一个商品：%d\n商品队列：", product);
        queue_print(&resource->queue);
        // 唤醒一个被阻塞的生产者
        pthread_cond_signal(&resource->producers_wait);
        /* ----------------------以上是临界区---------------------- */
        pthread_mutex_unlock(&resource->mutex);

        sleep(1); 
    }
    pthread_exit(NULL);
}
/* ---------------------------消费者线程--------------------------- */


int main(){
    // 设置随机种子
    srand(time(NULL));

    // 创建共享资源
    sharedRes_t sharedRes;
    /* ---------------------------初始化共享资源--------------------------- */
    // 初始化队列:最大商品个数是10个
    queue_init(&sharedRes.queue, MAX_SIZE);
    while(sharedRes.queue.size < 8){
        make_product(&sharedRes.queue); // 生产8个初始商品
    }

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&sharedRes.mutex, NULL);
    pthread_cond_init(&sharedRes.producers_wait, NULL);
    pthread_cond_init(&sharedRes.consumers_wait, NULL);
    /* ---------------------------初始化共享资源--------------------------- */

    // 创建3个生产者子线程
    pthread_t prod1, prod2, prod3;
    pthread_create(&prod1, NULL, producer, &sharedRes);
    pthread_create(&prod2, NULL, producer, &sharedRes);
    pthread_create(&prod3, NULL, producer, &sharedRes);

    // 创建2个消费者子线程
    pthread_t cons1, cons2;
    pthread_create(&cons1, NULL, consumer, &sharedRes);
    pthread_create(&cons2, NULL, consumer, &sharedRes);

    // 等待子线程运行结束
    pthread_join(prod1, NULL);
    pthread_join(prod2, NULL);
    pthread_join(prod3, NULL);
    pthread_join(cons1, NULL);
    pthread_join(cons2, NULL);
    
    return 0;
}